package xyz.mvfast.csv;

import com.opencsv.CSVReader;
import com.opencsv.CSVWriter;
import lombok.extern.slf4j.Slf4j;
import xyz.mvfast.annotations.Index;
import xyz.mvfast.util.DataConveter;

import java.io.*;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * @author fuzeqiang
 * @projectName easy-import
 * @title: CsvUtil
 * @date 2021/5/24 16:47
 */
@Slf4j
public class CsvUtil {


    /**
     * 默认使用GBK编码，解决Windows下默认GB2312的问题
     */
    private static final String DEFAULT_CHARSET_NAME = "GBK";


    /**
     * 默认一次读1000条数据
     */
    private static final int DEFAULT_BATCH_SIZE= 1000;


    /**
     * 默认第一行的行号
     */
    private static final int HEADER_LINE_NUMBER = 1;


    /**
     * 文件输出
     * @param out
     * @param header
     * @param list
     * @param clas
     */
    public <T> void writeEntityToFile(OutputStream out, String[] header, List<T> list, Class<T> clas){
        CSVWriter writer = null;
        try {
            writer = new CSVWriter(new OutputStreamWriter(out));
            if (header != null && header.length > 0){
                writer.writeNext(header);
            }
            int maxIndex = getMaxIndex(clas);
            for (T item : list) {
                writer.writeNext(conveterLineData(item, clas, maxIndex));
            }
        }catch (Exception e){
            log.error("write data to file failed ...", e);
        }finally {
            try {
                if (writer != null){
                    writer.flush();
                    writer.close();
                }
            }catch (Exception e){
                log.error("writer close failed...", e);
            }
        }
    }

    /**
     * 获取最大行号
     * @param clas
     * @param <T>
     * @return
     */
    private <T> int getMaxIndex(Class<T> clas) {
        Field[] fields = clas.getDeclaredFields();
        int maxIndex = 0;
        for (Field field : fields){
            Index index = field.getAnnotation(Index.class);
            if (index == null || index.ignore()){
                continue;
            }
            if (index.index() > maxIndex){
                maxIndex = index.index();
            }
        }
        return maxIndex;
    }


    /**
     * 单行数据转换
     * @param <T>
     * @param obj
     * @param clas
     * @param maxIndex
     * @return
     */
    private <T> String[] conveterLineData(T obj, Class<T> clas, int maxIndex) throws IllegalAccessException {
        Field[] fields = clas.getDeclaredFields();
        String[] data = new String[maxIndex];
        for (Field field : fields) {
            Index index = field.getAnnotation(Index.class);
            data[index.index() - 1] = field.get(obj).toString();
        }
        for (String item : data){
            if(item == null){
                item = "";
            }
        }
        return data;
    }


    /**
     * 解析csv文件(分段读)
     * @param file
     * @param claz
     * @param <T>
     * @return
     */
    public <T> List<T> readEntityFromFileWithBatch(File file, Class<T> claz, int start, int batchSize){
        List<T> list = null;
        CSVReader reader = null;
        try {
            list = new ArrayList<>();
            //先获取reader
            reader = new CSVReader(new InputStreamReader(new FileInputStream(file), DEFAULT_CHARSET_NAME));
            //先跳过需要
            reader.skip(start);
            //如果没设置行数，则使用默认的行数处理
            int size = batchSize > 0 ? batchSize : DEFAULT_BATCH_SIZE;
            for (int i=0; i<size; i++){
                String[] values = reader.readNext();
                if (values == null || values.length == 0){
                    break;
                }
                T data = conveterLineData(values, claz);
                if (data != null){
                    list.add(data);
                }
            }
        }catch (Exception e){
            list = Collections.emptyList();
            log.error("conveter data filed", e);
        }finally {
            try {
                if (reader != null) {
                    reader.close();
                }
            }catch (Exception e){
                log.error("reader close failed ...", e);
            }
        }
        return list;
    }


    /**
     * 全量解析csv文件
     * @param file
     * @param claz
     * @param <T>
     * @return
     */
    public <T> List<T> readEntityFromFile(File file, Class<T> claz){
        List<T> list = null;
        CSVReader reader = null;
        try {
            list = new ArrayList<>();
            //先获取reader
            reader = new CSVReader(new InputStreamReader(new FileInputStream(file), DEFAULT_CHARSET_NAME));
            //先跳过需要
            reader.skip(HEADER_LINE_NUMBER);
            while (true){
                String[] values = reader.readNext();
                if (values == null || values.length == 0){
                    break;
                }
                T data = conveterLineData(values, claz);
                if (data != null){
                    list.add(data);
                }
            }
        }catch (Exception e){
            list = Collections.emptyList();
            log.error("conveter data filed", e);
        }finally {
            try {
                if (reader != null) {
                    reader.close();
                }
            }catch (Exception e){
                log.error("reader close failed ...", e);
            }
        }
        return list;
    }


    /**
     * 转换一行数据
     * @param values
     * @param claz
     * @param <T>
     * @return
     */
    private <T> T conveterLineData(String[] values, Class<T> claz) throws NoSuchMethodException, IllegalAccessException,
            InvocationTargetException, InstantiationException {
        //对象初始化
        T obj = claz.getDeclaredConstructor().newInstance();
        //获取当前实例的所有属性
        Field[] fields = claz.getDeclaredFields();
        //遍历属性，并设置数值
        for (Field field : fields) {
            Index index = field.getAnnotation(Index.class);
            if (index == null || index.ignore()) {
                continue;
            }
            //先获取行数据
            String value = values[index.index()];
            //数据转换
            Object data = DataConveter.conveter(value, field.getType(), index.dateFormat());
            //设置数值
            field.setAccessible(true);
            field.set(obj, data);
        }
        return obj;
    }


}
